#include <qdatetime.h>
#include <qsqlrecord.h>
#include <qsqlerror.h>
#include <qsqlquery.h>
#include <qcolor.h>
#include <qprogressdialog.h>
#include <qapplication.h>

#include "databasemodel.h"

const QColor DatabaseModel::COLOR_OK = QColor(Qt::green);
const QColor DatabaseModel::COLOR_NO_TW = QColor(Qt::yellow);
const QColor DatabaseModel::COLOR_TOOMUCH_TW = QColor(Qt::cyan);
const QColor DatabaseModel::COLOR_TOOLESS_TW = QColor(255,132,132);

DatabaseModel::DatabaseModel(QObject *parent) :
    QSqlTableModel(parent)
{
}

int DatabaseModel::columnCount(const QModelIndex &parent) const
{
    int retVal = QSqlTableModel::columnCount(parent);

    if (retVal > 0)
        retVal += 3;

    return retVal;
}

QVariant DatabaseModel::data(const QModelIndex &idx, int role) const
{
    switch (role)
    {
    case Qt::DisplayRole:
    //case Qt::EditRole:
        switch(idx.column())
        {
        case Date:
            return QSqlTableModel::data(idx, role).toDate();
        case Begin:
        case End:
        case Break:
        case TwTime:
            return QSqlTableModel::data(idx, role).toTime();
        case Duration:
        {
            const QTime begin = QSqlTableModel::data(createIndex(idx.row(),Begin), role).toTime();
            const QTime end = QSqlTableModel::data(createIndex(idx.row(),End), role).toTime();
            const QTime breakTime = QSqlTableModel::data(createIndex(idx.row(), Break), role).toTime();
            const QTime duration(QTime::fromMSecsSinceStartOfDay(begin.msecsTo(end) - breakTime.msecsSinceStartOfDay()));
            if (duration.isValid())
                return duration;
            else
                return QString("Ungültig");
        }
        case Status:
            switch (calcStatus(idx.row()))
            {
            case TwOk:
                return QStringLiteral("OK");
            case TwNo:
                return QStringLiteral("Nichts");
            case TwTooMuch:
                return QStringLiteral("zu viel");
            case TwTooLess:
                return QStringLiteral("zu wenig");
            case TwDefault:
                return QString("Ungültig");
            }
        case Weekday:
            return QSqlTableModel::data(createIndex(idx.row(), Date), role).
                    toDate().toString(QStringLiteral("dddd"));
        }
        break;

    case Qt::TextAlignmentRole:
    {
        const int col = idx.column();
        if (col == Date || col == Comment || col == Status || col == Weekday)
            return QVariant(Qt::AlignVCenter | Qt::AlignLeft);
        else
            return QVariant(Qt::AlignVCenter | Qt::AlignRight);
        break;
    }

    case Qt::BackgroundRole:
        switch (calcStatus(idx.row()))
        {
        case TwOk:
            return COLOR_OK;
        case TwNo:
            return COLOR_NO_TW;
        case TwTooMuch:
            return COLOR_TOOMUCH_TW;
        case TwTooLess:
            return COLOR_TOOLESS_TW;
        case TwDefault:
            break;
        }
    }

    return QSqlTableModel::data(idx, role);
}

Qt::ItemFlags DatabaseModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags f = __super::flags(index);

    if (index.column() == TwTime)
        f &= ~Qt::ItemIsEnabled;

    return f;
}

bool DatabaseModel::addToday()
{
    QSqlRecord r = record(rowCount() - 1);
    const QDate today(QDate::currentDate());

    if (r.isEmpty() || r.value(QStringLiteral("date")).toDate() != today)
    {
        for (int i = 0; i < r.count(); i++)
            r.setGenerated(i, false);

        r.setGenerated(QStringLiteral("date"), true);
        r.setValue(QStringLiteral("date"), today);

        if (insertRecord(-1, r))
            return true;
        else
        {
            qDebug(qPrintable(lastError().text()));
            return false;
        }
    }
    else
        return true;
}

bool DatabaseModel::updateToday(const QTime &i_begin, const QTime &i_break)
{
    const int row = rowCount() - 1;
    QSqlRecord r = record(row);

    if (r.isEmpty() || r.value(QStringLiteral("date")).toDate() != QDate::currentDate())
        return false;

    for (int i = 0; i < r.count(); i++)
        r.setGenerated(i, false);

    r.setGenerated(QStringLiteral("begin"), true);
    r.setValue(QStringLiteral("begin"), i_begin);
    r.setGenerated(QStringLiteral("break"), true);
    r.setValue(QStringLiteral("break"), i_break);

    return setRecord(row, r);
}

bool DatabaseModel::updateTodaysEnd(int i_msecsToAdd)
{
    const int row = rowCount() - 1;
    QSqlRecord r = record(row);

    if (r.isEmpty() || r.value(QStringLiteral("date")).toDate() != QDate::currentDate())
        return false;

    for (int i = 0; i < r.count(); i++)
        r.setGenerated(i, false);

    r.setGenerated(QStringLiteral("end"), true);
    r.setValue(QStringLiteral("end"), QTime::currentTime().addMSecs(i_msecsToAdd));

    return setRecord(row, r);
}

int DatabaseModel::updateTisowareData(QString& o_errorText,
                                      const QDate& i_dateFrom, const QDate& i_dateTo)
{
    QSqlDatabase dbTiso = QSqlDatabase::database("tisoware", false);

    if (!dbTiso.open())
    {
        o_errorText = "Failed to setup tisoware database connection!\n"
                + dbTiso.lastError().text();
        return -1;
    }

    const int rows = rowCount();
    QProgressDialog progress("Lade Daten...", "Abbrechen", 0, rows);
    progress.setWindowModality(Qt::WindowModal);
    progress.show();

    const QString username = qgetenv("USERNAME");
    const QString query = QString("SELECT BEZEITNS, BEZEITNM FROM BUCHEINZ "
                                  "WHERE BENNR = '%1' AND BEDATE = %2").arg(username);
    int days = 0;
    QSqlQuery q(dbTiso);
    q.setForwardOnly(true);

    //qDebug("From: %s", qPrintable(i_dateFrom.toString()));
    //qDebug("To: %s", qPrintable(i_dateTo.toString()));

    for (int i = 0; i < rows && !progress.wasCanceled(); i++)
    {
        progress.setValue(i);
        qApp->processEvents();
        QSqlRecord rec(record(i));
        const QDate& date = rec.value(QStringLiteral("date")).toDate();
        //qDebug("date: %s", qPrintable(date.toString()));

        if (date < i_dateFrom)
            continue;
        else if (date > i_dateTo)
            break;

        const int dateNr = date.toString(QStringLiteral("yyyyMMdd")).toInt();

        if(q.exec(query.arg(dateNr)))
        {
            //qDebug(qPrintable(q.executedQuery()));
            //qDebug(qPrintable(query.arg(date)));
            int minutes = 0;

            while(q.next())
            {
                minutes += q.value(QStringLiteral("BEZEITNS")).toInt() * 60
                        + q.value(QStringLiteral("BEZEITNM")).toInt();
                //qDebug("%d__ %d:%d", date, q.value(QStringLiteral("BEZEITNS")).toInt() * 60, q.value(QStringLiteral("BEZEITNM")).toInt());
            }

            if (minutes > 0)
            {
                ++days;
                //qDebug(qPrintable(QTime::fromMSecsSinceStartOfDay(minutes * 60 * 1000).toString()));

                for (int col = 0; col < rec.count(); col++)
                    rec.setGenerated(col, false);

                rec.setGenerated(QStringLiteral("tw_time"), true);
                rec.setValue(QStringLiteral("tw_time"),
                             QTime::fromMSecsSinceStartOfDay(minutes * 60 * 1000));

                //qDebug(qPrintable(rec.value(QStringLiteral("tw_time")).toTime().toString()));

                if (!setRecord(i, rec))
                {
                    o_errorText = "Failed to update the local database!\n"
                            + lastError().text();
                    days = -1;
                    break;
                }
            }
        }
        else
        {
            o_errorText = "Failed to query tisoware data!\n"
                    + QString("[%1]\n").arg(q.executedQuery())
                    + dbTiso.lastError().text();
            break;
        }
    }

    progress.setValue(rows);

    dbTiso.close();
    return days;
}

QDate DatabaseModel::minimumDate() const
{
    return data(createIndex(0, Date)).toDate();
}

QDate DatabaseModel::maximumDate() const
{
    return data(createIndex(rowCount() - 1, Date)).toDate();
}

int DatabaseModel::sumTisoware() const
{
    QSqlQuery q;
    q.setForwardOnly(true);

    if (q.exec("SELECT SUM(strftime('%s', tw_time) - strftime('%s', '00:00:00')) "
               "twSum FROM TimeToGoHome") && q.next())
    {
        //qDebug("%d", q.value("twSum").toInt());
        return q.value("twSum").toInt();
    }

    return 0;
}

int DatabaseModel::sumWork() const
{
    int sum = 0;

    for (int i = 0; i < rowCount(); i++)
    {
        const QVariant& value = data(createIndex(i, Duration));
        if (value.type() == QVariant::Time)
        {
            sum += value.toTime().msecsSinceStartOfDay() / 1000;
        }
    }

    return sum;
}

DatabaseModel::TwStatus DatabaseModel::calcStatus(int i_row) const
{
    const QVariant& duration = data(createIndex(i_row, Duration));
    const QTime& twTime = data(createIndex(i_row, TwTime)).toTime();

    if (duration.type() != QVariant::Time)
    {
        if (twTime.isValid() && twTime.msecsSinceStartOfDay() > 0)
            return TwTooMuch;
        else
            return TwDefault;
    }

    if (!twTime.isValid() || twTime.msecsSinceStartOfDay() == 0)
        return TwNo;

    const QTime& durationTime = duration.toTime();
    const int msecs = twTime.msecsTo(durationTime);

    // Mehr als halbe Stunde zu wenig rückgemeldet
    if (msecs >= 30 * 60 * 1000)
        return TwTooLess;
    // Mehr als halbe Stunde zu viel rückgemeldet
    else if (msecs <= - 30 * 60 * 1000)
        return TwTooMuch;
    else
        return TwOk;
}

